package db

import (
	"fmt"
	"net/http"
	"net/url"
	"os"

	"gitee.com/joseelin/goserve/req"
	"gitee.com/joseelin/goserve/res"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var global *gorm.DB

func initialize(conn string) (*gorm.DB, error) {
	if global == nil {
		u, err := url.Parse(conn)
		if err != nil {
			return nil, fmt.Errorf("MYSQL连接参数非法")
		}
		q := u.Query()
		if q.Get("charset") == "" {
			q.Set("charset", "utf8mb4")
		}
		if q.Get("collation") == "" {
			q.Set("collation", "utf8_general_ci")
		}
		if q.Get("parseTime") == "" {
			q.Set("parseTime", "true")
		}
		if q.Get("loc") == "" {
			q.Set("loc", "Asia/Shanghai")
		}
		if q.Get("autocommit") == "" {
			q.Set("autocommit", "true")
		}
		u.RawQuery = q.Encode()
		db, err := gorm.Open("mysql", u.String())
		if err != nil {
			return nil, err
		}
		db.DB().SetMaxIdleConns(0)
		db.DB().SetMaxOpenConns(1)
		db.DB().SetConnMaxLifetime(0)
		global = db
	}
	return global, nil
}
func Share() *gorm.DB {
	return global
}
func New() *gorm.DB {
	return global.New()
}
func Model(x interface{}) *gorm.DB {
	return global.New().Model(x)
}
func Count(x interface{}, args ...interface{}) int {
	count := 0
	scope := global.New().Model(x)
	if len(args) > 0 {
		scope = scope.Where(args[0], args[1:]...)
	}
	scope.Count(&count)
	return count
}
func Save(x interface{}) *gorm.DB {
	return global.New().Save(x)
}
func Delete(value interface{}, where ...interface{}) *gorm.DB {
	return global.New().Delete(value, where...)
}
func Table(x interface{}, alias string) *gorm.DB {
	table := global.New().NewScope(x).TableName()
	return global.New().Table(fmt.Sprintf("%s as %s", table, alias)).Select("*")
}
func TableName(x interface{}) string {
	return global.New().NewScope(x).TableName()
}
func Join(query string, args ...interface{}) string {
	tables := make([]interface{}, len(args))
	for k, v := range args {
		tables[k] = global.New().NewScope(v).TableName()
	}
	q := fmt.Sprintf(query, tables...)
	return q
}
func Init() error {
	conn, ok := os.LookupEnv("mysql_conn_str")
	if !ok {
		return fmt.Errorf("请配置mysql连接字符env.mysql_conn_str")
	}
	_, e := initialize(conn)
	return e
}

type SearchData struct {
	Value string `json:"value"`
	Regex bool   `json:"regex"`
}
type OrderData struct {
	Column int    `json:"column"`
	Dir    string `json:"dir"`
}
type ColumnData struct {
	Data   string      `json:"data"`
	Name   string      `json:"name"`
	Search *SearchData `json:"search"`
}
type PageData struct {
	Draw    int           `json:"draw"`
	Length  int           `json:"length"`
	Start   int           `json:"start"`
	Columns []*ColumnData `json:"columns"`
	Order   []*OrderData  `json:"order"`
}

func Page(w http.ResponseWriter, r *http.Request, d *gorm.DB, out interface{}, orders ...interface{}) {
	q := &PageData{}
	if e := req.Json(r, q); e != nil {
		res.Bad(w, e.Error())
		return
	}

	for _, c := range q.Columns {
		if c.Search != nil && c.Search.Value != "" {
			if c.Search.Regex {
				d = d.Where(fmt.Sprintf("%s = ?", c.Name), c.Search.Value)
			} else {
				d = d.Where(fmt.Sprintf("%s LIKE ?", c.Name), fmt.Sprintf("%%%v%%", c.Search.Value))
			}
		}
	}
	if len(orders) > 0 && orders[0] != nil {
		d = d.Order(fmt.Sprintf("%v", orders[0]))
	}
	if q.Order != nil && len(q.Order) > 0 {
		for _, o := range q.Order {
			d = d.Order(fmt.Sprintf("%s %s", q.Columns[o.Column].Name, o.Dir))
		}
	}
	if len(orders) > 1 && orders[1] != nil {
		d = d.Order(fmt.Sprintf("%v", orders[1]))
	}
	total := 0
	d.Count(&total)
	d = d.Offset(q.Start).Limit(q.Length)
	d.Scan(out)
	res.Success(w, map[string]interface{}{
		"draw":            q.Draw,
		"data":            out,
		"recordsTotal":    total,
		"recordsFiltered": total,
	})
	return
}
